package a05;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;

import java.util.List;
import java.util.Set;

/**
 * 模拟实现解析@Bean的后处理器
 */
public class MyAtBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        /**
         * 整体思路:
         * 1.拿到@Bean标注的方法的信息。(我们知道，@Bean标注的方法里会生成bean，而这个返回值Bean会被spring拿到管理)
         *   所以我们可以理解为其为FactoryMethod，即生产bean的方法
         * 2.通过BeanDefinitionBuilder根据这些方法信息去构建BeanDefinition。
         *
         * 设计到的几个工具类的介绍：
         * 1.MetadataReaderFactory 一个获取class对象里面的各种信息、包括注解、方法等
         * 2.BeanDefinitionBuilder 构建BeanDefinition。spring中BeanDefinition应该是一个很重要的概念，目前我感觉
         *   所有的Bean应该都现有BeanDefinition。所以各种解析各种注解的后处理器都能看到BeanDefinitionBuilder的身影
         */
        try {
            CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory();
            MetadataReader reader = factory.getMetadataReader(new ClassPathResource("a05/Config.class"));
            Set<MethodMetadata> methods = reader.getAnnotationMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata method : methods) {
                String initMethod = method.getAnnotationAttributes(Bean.class.getName()).get("initMethod").toString();
                BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
                //给build指定“方法名字”和“工厂方法所在的bean”，当然这个bean需要以及注册到spring里呢
                builder.setFactoryMethodOnBean(method.getMethodName(), "config");
                //对于@bean的方法参数的注入，需要设置这个注入的模式，这里先记住选AUTOWIRE_CONSTRUCTOR就行
                builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
                //这里是设置初始化bean时执行的bean里的初始化函数，也就是@Bean的属性值initMethod的解析
                if (initMethod.length() > 0) {
                    builder.setInitMethodName(initMethod);
                }
                AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
                registry.registerBeanDefinition(method.getMethodName(), beanDefinition);

            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
